/****************************************************************************
  Copyright (C) 2010 Wanderson Regis Silva
  All rights reserved
  Contact: Wanderson Regis Silva (wanderson.regis@ymail.com)

  This file is part of OpenEditor project

  This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/

#include <qapplication.h>
#include <qfile.h>
#include <qfiledialog.h>
#include <qfileinfo.h>
#include <qmessagebox.h>
#include <qpainter.h>
#include <qtextstream.h>

#include "codeedit.h"

void CodeEdit::lineNumberAreaPaintEvent(QPaintEvent *event) {
  QPainter painter(lines);
  painter.fillRect(event->rect(), QColor(0xF0, 0xF0, 0xF0));

  QTextBlock block = firstVisibleBlock();
  int blockNumber = block.blockNumber();
  int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
  int bottom = top + (int)blockBoundingRect(block).height();

  while(block.isValid() && top <= event->rect().bottom()) {
    if(block.isVisible() && bottom >= event->rect().top()) {
      QString number = QString::number(blockNumber + 1);
      painter.setPen(QColor(0xA0, 0xA0, 0xA0));
      painter.drawText(0, top, lines->width() - 2, fontMetrics().height(), Qt::AlignRight, number);
    }

    block = block.next();
    top = bottom;
    bottom = top + (int)blockBoundingRect(block).height();
    ++blockNumber;
  }
}

int CodeEdit::lineNumberAreaWidth(void) const {
  int digits = 1;
  int max = qMax(1, blockCount());
  while(max >= 10) {
    max /= 10;
    ++digits;
  }

  int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;

  return space + 9;
}

bool CodeEdit::loadFile(const QString &fileName) {
  if( ! fileName.isEmpty())
    __fileName = fileName;

  if(__fileName.isEmpty()) {
    QMessageBox::warning(parentWidget()->parentWidget(), qApp->applicationName(), tr("There is no file name!"));
    return false;
  }

  QFile file(__fileName);

  if( ! file.open(QFile::ReadOnly | QFile::Text)) {
    QMessageBox::warning(parentWidget()->parentWidget(), qApp->applicationName(), tr("Cannot read file %1:\n%2").arg(__fileName).arg(file.errorString()));
    return false;
  }

#ifndef QT_NO_CURSOR
  QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
  QTextStream in(&file);
  setText(in.readAll());
#ifndef QT_NO_CURSOR
  QApplication::restoreOverrideCursor();
#endif

  document()->setModified(false);

  return true;
}

bool CodeEdit::saveFile(const QString &fileName) {
  if( ! fileName.isEmpty())
    __fileName = fileName;

  if(__fileName.isEmpty()) {
    QMessageBox::warning(parentWidget()->parentWidget(), qApp->applicationName(), tr("There is no file name!"));
    return false;
  }

  QFile file(__fileName);

  if(!file.open(QFile::WriteOnly | QFile::Text)) {
    QMessageBox::warning(parentWidget()->parentWidget(), qApp->applicationName(), tr("Cannot write file %1:\n%2.").arg(__fileName).arg(file.errorString()));
    return false;
  }

  QTextStream out(&file);
#ifndef QT_NO_CURSOR
  QApplication::setOverrideCursor(Qt::WaitCursor);
#endif
  out << toPlainText();
#ifndef QT_NO_CURSOR
  QApplication::restoreOverrideCursor();
#endif

  document()->setModified(false);

  return true;
}

void CodeEdit::zoomIn(const int &range) {
  QFont font = this->font();

  int size = font.pointSize() + range;

  if(size > 128)
    size = 128;

  font.setPointSize(size);

  setFont(font);
}

void CodeEdit::zoomOut(const int &range) {
  QFont font = this->font();

  int size = font.pointSize() - range;
  if(size < 6)
    size = 6;

  font.setPointSize(size);

  setFont(font);
}

void CodeEdit::closeEvent(QCloseEvent *event) {
  if(document()->isModified()) {
    int ret = QMessageBox::warning(parentWidget()->parentWidget(), qApp->applicationName(), tr("This document has been modified.\n"
                                   "Do you want to save your changes?"), QMessageBox::Save | QMessageBox::Discard |
                                   QMessageBox::Cancel);

    switch(ret) {
    default:
      event->accept();
      break;
    case QMessageBox::Cancel:
      event->ignore();
      break;
    case QMessageBox::Save:
      {
        if(fileName().isEmpty()) {
          QString fileName = QFileDialog::getSaveFileName(parentWidget()->parentWidget(), tr("Save As"));

          if(fileName.isEmpty()) {
            event->ignore();
            return;
          }

          __fileName = fileName;
        }

        if(saveFile(__fileName))
          event->accept();
        else
          event->ignore();
      }
      break;
    }
  } else {
    event->accept();
  }
}

void CodeEdit::resizeEvent(QResizeEvent *event) {
  QPlainTextEdit::resizeEvent(event);

  QRect cr = contentsRect();
  lines->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height()));
}

void CodeEdit::updateLineNumberAreaWidth(const int &/* newBlockCount */) {
  setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
}

void CodeEdit::highlightCurrentLine(void) {
  QList<QTextEdit::ExtraSelection> extraSelections;

  if( ! isReadOnly()) {
    QTextEdit::ExtraSelection selection;

    selection.format.setBackground(QColor(0xE9, 0xF9, 0xF9));
    selection.format.setProperty(QTextFormat::FullWidthSelection, true);
    selection.cursor = textCursor();
    selection.cursor.clearSelection();
    extraSelections.append(selection);
  }

  setExtraSelections(extraSelections);
}

void CodeEdit::updateLineNumberArea(const QRect &rect, int dy) {
  if(dy)
    lines->scroll(0, dy);
  else
    lines->update(0, rect.y(), lines->width(), rect.height());

  if(rect.contains(viewport()->rect()))
    updateLineNumberAreaWidth(0);
}

void CodeEdit::init() {
  setLineWrapMode(NoWrap);

  QFont font;
#ifdef Q_WS_X11
  font.setFamily("Monospace");
#else
  font.setFamily("Courier New");
#endif
  font.setPointSize(9);
  setFont(font);

  lines = new LineNumberArea(this);

  connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int)));
  connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int)));
  connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(highlightCurrentLine()));

  highlightCurrentLine();
}
